package com.lin.benchmade.pinyin;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.lang.StringUtils;

import com.lin.benchmade.utils.PinYinUtils;
/**
 * 拼音转换器,把一个String转换为拼音.使用枚举实现singletan
 * @Author : xiaolin
 * @Date : 2012-11-8 下午6:04:07
 */
public enum PinYinConverter {

	INSTANCE;

	private final String DICT_FILE = "/com/lin/benchmade/pinyin/Polyphone.dic";
	//多音字的 词和这个词的拼音 "长度@chang'du"
	private final Map<String, String> polyphonic_pinyin = new HashMap<String, String>(512);
	//traditional Chinese -> Simplified Chinese   mapping
	private final Map<Character, Character> tsMapping = new HashMap<Character, Character>(64);
	//长度 - 長度
	private final Map<String, String> scWord2tcWord = new HashMap<String, String>(256);

	/**
	 * 实例化时先填充各个字典
	 * Date : 2012-11-8
	 */
	private PinYinConverter() {
		try {
			InputStream is = PinYinConverter.class.getResourceAsStream(DICT_FILE);
			InputStreamReader isr = new InputStreamReader(is, "utf-8");
			BufferedReader br = new BufferedReader(isr);
			String line;
			while ((line = br.readLine()) != null) {
				String[] array = StringUtils.split(line, "@");
				polyphonic_pinyin.put(array[0], array[1]);
				if (array.length == 3) {// 如果是3，说明有繁体字对应
					for (int i = 0; i < array[0].length(); i++) {// 找到简体0，和繁体3不同的就是做了映射的
						if (array[0].charAt(i) != array[2].charAt(i)) {
							tsMapping.put(array[2].charAt(i), array[0].charAt(i));
						}
					}
					scWord2tcWord.put(array[0], array[2]);
				}
			}
			br.close();
			isr.close();
			is.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 返回的是数组不是String的原因是字符中可能含有非中文,并且返回String后,没法区分每个字的拼音.
	 * Date : 2012-11-8
	 * @param keyWord 要转换的关键字
	 * @param type 要转换的类型
	 * @return 转换后的数组
	 */
	public String[] toPinyin(String keyWord, ConverterType type){
		char[] data = keyWord.toCharArray();
		String[] pinyinArray = new String[data.length];
		int offset = 0;//
		int interval = 0;// 每一次最大匹配的长度
		String word, pinyin;
		String[] splitArray;
		while (offset < data.length) {
			interval = data.length - offset >= 4 ? 4 : data.length - offset;
			while (interval > 0) {
				word = new String(data, offset, interval);
				pinyin = polyphonic_pinyin.get(word);
				if (pinyin != null) {
					splitArray = StringUtils.split(pinyin, "'");
					// 遇到多音字，直接把value用’隔开后，放置到目标数组。
					System.arraycopy(splitArray, 0, pinyinArray, offset, splitArray.length);
					offset += interval;
					break;
				}
				interval--;
				if (interval == 0) {// 如果end==0到头了，但是还没有匹配上，那说明匹配不上字典，是一个单字
					if (PinYinUtils.isChineseChar(data[offset])) {// 如果是汉字，那么转换
						pinyinArray[offset] = PinYinUtils.char2Spell(data[offset]);
						offset++;
					} else {// 如果是其他字符，那么首先将null放入数组，然后依次判断后面的字符，如果还是非汉字，那么直接填null，为了保证含有英文的性能，在readme中有介绍
						switch (type) {
							case REPLACE_INVALID:
								pinyinArray[offset] = null;
								break;
							case INCLUDE_INVALID:
								pinyinArray[offset] = Character.toString(data[offset]);
								break;
							default:
								break;
						}
						offset++;
					}
				}
			}
		}
		return pinyinArray;
	}

	/**
	 * 解码繁体代替简体的Stirng
	 * Date : 2012-11-8
	 * @param keyWord 需要解码的String
	 * @return 转换后的简体String
	 */
	public String traditionalChineseDecode(String keyWord) {
		for (int i = 0; i < keyWord.length(); i++) {
			if (tsMapping.containsKey(keyWord.charAt(i))) {
				keyWord = StringUtils.replaceChars(keyWord, keyWord.charAt(i), tsMapping.get(keyWord.charAt(i)));
			}
		}
		return keyWord;
	}

	/**
	 * 返回转换后的拼音,并且填充一个繁体字代替多音字的char[]
	 * @param pinyin 待转换的汉字，必须都是汉字
	 * @return
	 */
	public String[] encodeFillAndToPinyin(char[] data, char[] traditionalChinese) {
		String[] pinyinArray = new String[data.length];
		int offset = 0;//
		int interval = 0;// 每一次最大匹配的长度
		String word, pinyin;
		String[] splitArray;
		while (offset < data.length) {
			interval = data.length - offset >= 4 ? 4 : data.length - offset;
			while (interval > 0) {
				word = new String(data, offset, interval);
				pinyin = polyphonic_pinyin.get(word);
				if (pinyin != null) {
					splitArray = StringUtils.split(pinyin, "'");
					// 遇到多音字，直接把value用’隔开后，放置到目标数组。
					System.arraycopy(splitArray, 0, pinyinArray, offset, splitArray.length);
					if (scWord2tcWord.containsKey(word)) {
						word = scWord2tcWord.get(word);
					}
					System.arraycopy(word.toCharArray(), 0, traditionalChinese, offset, word.length());
					offset += interval;
					break;
				}
				interval--;
				if (interval == 0) {// 如果end==0到头了，但是还没有匹配上，那说明匹配不上字典，是一个单字
					if (PinYinUtils.isChineseChar(data[offset])) {// 如果是汉字，那么转换
						pinyinArray[offset] = PinYinUtils.char2Spell(data[offset]);
						traditionalChinese[offset] = data[offset];
						offset++;
					} else {// 如果是其他字符，那么首先将null放入数组，然后依次判断后面的字符，如果还是非汉字，那么直接填null，为了保证含有英文的性能，在readme中有介绍
						pinyinArray[offset] = null;
						traditionalChinese[offset] = data[offset];
						offset++;
						/*
						 * while(!PinYinUtils.isChineseChar(value[offset])){
						 * pinyinArray[offset] = null; offset ++;
						 * }暂时注释掉，因为小说英文字母很少
						 * ，所以不用验证，MP3这样多的时候可以进行判断，见readme，为了防止正向最大匹配 对英文处理性能的浪费
						 */
					}
				}
			}
		}
		return pinyinArray;
	}

	public enum ConverterType {
		REPLACE_INVALID, INCLUDE_INVALID;
	}
}
